__doc__ = '''
A lexer for a subset of x4gl syntax.
'''
__copyright__ = '(c)2007 Mark J. Rees http://jmmjsolutions.com'
__license__ = ''' GNU General Public License

This program is free software; 
you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA. '''

import ply.lex as lex

states = (
    ('end', 'exclusive'),
)

reserved = {
    'main'      :   'MAIN',
    'define'    :   'DEFINE',
    'let'       :   'LET',
    'if'        :   'IF',
    'then'      :   'THEN',
    'else'      :   'ELSE',
    'endif'     :   'END_IF',
    'while'     :   'WHILE',
    'endwhile'  :   'END_WHILE',
    'for'       :   'FOR',
    'endfor'    :   'END_FOR',
    'foreach'   :   'FOREACH',
    'endforeach':   'END_FOREACH',
    
    'true'      :   'TRUE',
    'false'     :   'FALSE',
    
    'integer'   :   'INTEGER',
    'smallint'  :   'SMALLINT',
    'decimal'   :   'DECIMAL',
    'money'     :   'MONEY',
    'float'     :   'FLOAT',
    'date'      :   'DATE',
    'datetime'  :   'DATETIME',
    'interval'  :   'INTERVAL',
    'string'    :   'STRING',
    'char'      :   'CHAR',
    'varchar'   :   'VARCHAR',
    
}

tokens = tuple(reserved.values()) + (
    'NUMBER',
    'LSTRING',
    'PLUS',
    'MINUS',
    'TIMES',
    'DIVIDE',
    'LPAREN',
    'RPAREN',
    'RBRACK',
    'LBRACK',
    'ASSIGN',
    'IDENTIFIER',
)

#t_NUMBER    = r'(0[xX][a-fA-F0-9]+)|(0[0-9]+)|([0-9]+)|([0-9]*\.[0-9]+)|([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+[eE][+-]?[0-9]+)|([0-9]+\.[0-9]*[eE][+-]?[0-9]+)'
t_PLUS      = r'\+'
t_MINUS     = r'-'
t_TIMES     = r'\*'
t_DIVIDE    = r'/'
t_LPAREN    = r'\('
t_RPAREN    = r'\)'
t_RBRACK    = r'\]'
t_LBRACK    = r'\['
t_ASSIGN    = r'='
t_LSTRING    = r'(\"([^\\\n\r]|(\\.))*?\")|(\'([^\\\n\r]|(\\.))*?\')'

def t_CHAR(t):
    r'char\([0-9]*\)|char'
    return t

def t_VARCHAR(t):
    r'varchar\([0-9]*,[0-9]+\)'
    return t
    
def t_NUMBER(t):
    r'\d+'
    try:
        t.value = int(t.value)
    except ValueError:
        print "Integer value too large", t.value
        t.value = 0
    return t

def t_IDENTIFIER(t):
    r'[a-zA-Z_][a-zA-Z_0-9]*'
    if t.value.lower() == 'end':
        t.lexer.begin('end')
    else:
        t.type = reserved.get(t.value.lower(),'IDENTIFIER')    # Check for reserved words
        return t

def t_NEWLINE(t):
    r'\r\n|\r|\n'
    t.lexer.lineno += 1
    pass

# Comments including multi-line
def t_mcomment(t):
    r'({(.|\n|\r)*?})|(\#.*)'
    if t.value.count('\r\n'):
        t.lexer.lineno += t.value.count('\r\n')
    else:
        t.lexer.lineno += t.value.count('\n')
    pass

# Ignored characters
t_ignore = ' \t'

def t_error(t):
    print "Illegal character '%s'" % t.value[0]
    t.lexer.skip(1)

# Conditional END state rules
def t_end_handler(t):
    r'[a-zA-Z][a-zA-Z_0-9]*'
    value = t.value.lower()
    if value == 'if':
        t.type = 'END_IF'
    elif value == 'while':
        t.type = 'END_WHILE'
    elif value == 'for':
        t.type = 'END_FOR'
    elif value == 'foreach':
        t.type = 'END_FOREACH'
    else:
        t.type = 'END_ERROR'
    t.lexer.begin('INITIAL')
    return t

t_end_ignore = ' \t'

def t_end_error(t):
    t.lexer.skip(1)

import re
lexer = lex.lex(reflags=re.IGNORECASE)

if __name__ == "__main__":

    import sys
    
    if len(sys.argv) > 1:
        lexer.input(open(sys.argv[1], "r").read())
    else:
        lexer.input(raw_input('x4gl lexer> '))
    while 1:
        tok = lexer.token()
        if not tok: break
        print tok


